package com.orange.delivery.presentation.panel;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.validation.validator.StringValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.security.access.AccessDeniedException;

import com.francetelecom.clara.component.wicket.app.form.OForm;
import com.francetelecom.clara.component.wicket.app.page.ODynamicFeedbackPanel;
import com.francetelecom.clara.component.wicket.app.popin.OPopinAjaxCloseLink;
import com.francetelecom.clara.component.wicket.app.popin.OPopinContainer;
import com.orange.delivery.application.ManageMarket;
import com.orange.delivery.model.MarketBean;
import com.orange.delivery.presentation.event.CreateOrUpdateMarketEvent;
import com.orange.delivery.technicalservice.exception.InvalidMarketException;
import com.orange.delivery.technicalservice.exception.NotFoundException;
import com.orange.delivery.technicalservice.exception.TechnicalException;

public abstract class CreateOrUpdateMarketPanel extends Panel {
	private static final long serialVersionUID = 1L;

	private static final Logger LOG = LoggerFactory.getLogger(CreateOrUpdateMarketPanel.class.getName());

	// application layer manager
	@SpringBean(name = "manageMarketApplication")
	protected ManageMarket manageMarket;

	// new or to-be-modified market
	private MarketBean marketBean;

	private MarketForm marketForm;

	// popin
	private OPopinContainer modalPopIn;

	/**
	 * Available actions are creation and modification
	 * 
	 */
	protected enum Action {
		CREATED("info.marketCreated"), UPDATED("info.marketUpdated");
		private String property;

		private Action(String property) {
			this.property = property;
		}

		@Override
		public String toString() {
			return property;
		}
	}

	public CreateOrUpdateMarketPanel(String id, final MarketBean marketBean, OPopinContainer modalPopIn) {
		super(id);
		this.marketBean = marketBean;
		this.modalPopIn = modalPopIn;

		createCommonComponents();
		createComponents();
	}

	public void setMarketBean(final MarketBean marketBeanForm) {
		this.marketBean = marketBeanForm;
		this.marketForm.setModel(new CompoundPropertyModel<MarketBean>(marketBean));
	}

	protected abstract void createComponents();

	/**
	 * call correct persist method for market creation/modification
	 * 
	 * @param marketBean
	 * @return The creation/modification confirmation message
	 * @throws InvalidMarketException
	 *             if market create/update fails
	 * @throws NotFoundException
	 *             if market does not exist
	 */

	protected abstract Action createOrUpdateMarket(MarketBean marketBean) throws InvalidMarketException, NotFoundException;

	private void createCommonComponents() {
		LOG.debug("createPanelFormCreate");

		// =====================================================
		// panel for warning messages
		// =====================================================

		final ODynamicFeedbackPanel feedback = new ODynamicFeedbackPanel("feedback");
		feedback.setOutputMarkupId(true);
		feedback.setOutputMarkupPlaceholderTag(true);
		add(feedback);
		marketForm = new MarketForm("createForm", marketBean);
		marketForm.setOutputMarkupId(true);
		marketForm.add(feedback);
		final WebMarkupContainer dataContainerForm = new WebMarkupContainer("dataForm");
		add(dataContainerForm);
		dataContainerForm.setOutputMarkupId(true);
		dataContainerForm.setOutputMarkupPlaceholderTag(true);

		dataContainerForm.add(marketForm);

		// ==============================================================
		// Button to form for create/updating market
		// ==============================================================

		IndicatingAjaxButton createMarketButton = new IndicatingAjaxButton("okButton", marketForm) {
			private static final long serialVersionUID = 1L;

			@Override
			protected void onSubmit(AjaxRequestTarget target, Form<?> form) {

				MarketBean marketBeanForm = (MarketBean) form.getModelObject();

				try {
					Action action = createOrUpdateMarket(marketBeanForm);

					success(getString(action.toString(), new Model<Object[]>(new Object[] { marketBeanForm.getName() })));

					target.add(feedback);
					target.add(form);
					target.add(dataContainerForm);

					// ==============================================================
					// send create or modif market event
					// ==============================================================
					send(getPage(), Broadcast.BREADTH, new CreateOrUpdateMarketEvent(target));

				} catch (NotFoundException e) {
					// functional exception : display warning message
					LOG.debug("market {} not found", marketBeanForm.getName(), e);
					warn(getString("warning.marketHasDisappeared", new Model<Object[]>(new Object[] { marketBeanForm.getName() })));
					target.add(feedback);
				} catch (OptimisticLockingFailureException e) {
					// Known technical exception : display warning message
					LOG.debug("market {} has changed", marketBeanForm.getName(), e);
					warn(getString("warning.marketHasChanged", new Model<Object[]>(new Object[] { marketBeanForm.getName() })));
					target.add(feedback);

				} catch (InvalidMarketException e) {
					// Functional exception : display warning message
					LOG.debug("market {} already exists", marketBeanForm.getName(), e);
					warn(getString("warning.marketAlreadyExists", new Model<Object[]>(new Object[] { marketBeanForm.getName() })));
					target.add(feedback);
				} catch (AccessDeniedException e) {
					// Functional exception with Spring Security @PreAuthorize annotation : display warning message
					LOG.debug("not allowed to update market {}", marketBeanForm.getName(), e);
					warn(getString("warning.marketNotAllowed", new Model<Object[]>(new Object[] { marketBeanForm.getName() })));
					target.add(feedback);
				} catch (TechnicalException e) {
					// Technical exception : generic error message
					LOG.error("a technical exception has occured", e);
					error(getString("error.internal"));
					target.add(feedback);
				}
			}

			@Override
			protected void onError(AjaxRequestTarget target, Form<?> form) {
				// show wicket message if form validation fails
				target.add(feedback);
			}

		};
		marketForm.add(createMarketButton);

		final OPopinAjaxCloseLink<Void> closeModalPopIn = new OPopinAjaxCloseLink<Void>("koButton", modalPopIn) {
			private static final long serialVersionUID = 1L;
		};

		marketForm.add(closeModalPopIn);
	}

	/**
	 * private class representing the market creation form
	 * 
	 * @param <T> The {@link MarketBean} filled by the creation form
	 */
	private class MarketForm extends OForm<MarketBean> {

		private static final long serialVersionUID = 1L;

		public MarketForm(String id, MarketBean marketBean) {
			super(id, new CompoundPropertyModel<MarketBean>(marketBean));

			// CoumpoundPropertyModel on MarketBean
			add(new RequiredTextField<String>("name").add(StringValidator.minimumLength(5)));
			add(new TextArea<String>("description"));
		}
	}

}
